package cn.kow.kmall.controller;

import cn.kow.kmall.common.enums.OrderStatusEnum;
import cn.kow.kmall.common.enums.OrderRefundStatusEnum;
import cn.kow.kmall.common.enums.KmallResultEnum;
import cn.kow.kmall.config.WXAccountConfig;
import cn.kow.kmall.pojo.entity.Order;
import cn.kow.kmall.pojo.entity.OrderRefund;
import cn.kow.kmall.service.OrderService;
import cn.kow.kmall.core.exception.ServiceException;
import cn.kow.kmall.core.response.KmallResult;
import cn.kow.kmall.core.util.JsonUtils;
import cn.kow.kmall.common.wxpay.WXPay;
import cn.kow.kmall.common.wxpay.WXPayConfig;
import cn.kow.kmall.common.wxpay.WXPayConstants;
import cn.kow.kmall.common.wxpay.WXPayUtil;
import cn.kow.kmall.service.OrderRefundService;
import lombok.extern.slf4j.Slf4j;
import org.apache.commons.codec.digest.DigestUtils;
import org.apache.commons.lang3.StringUtils;
import org.bouncycastle.jce.provider.BouncyCastleProvider;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Controller;
import org.springframework.transaction.annotation.Transactional;
import org.springframework.util.Base64Utils;
import org.springframework.validation.annotation.Validated;
import org.springframework.web.bind.annotation.*;

import javax.crypto.Cipher;
import javax.crypto.spec.SecretKeySpec;
import javax.validation.constraints.NotEmpty;
import java.security.Security;
import java.text.SimpleDateFormat;
import java.util.Date;
import java.util.HashMap;
import java.util.Map;

@Slf4j
@Validated
@Controller
@RequestMapping("/pay")
public class PaymentController {

    @Autowired
    private OrderService orderService;

    @Autowired
    private OrderRefundService orderRefundService;

    @Autowired
    private WXAccountConfig wxPayConfig;

    /**
     * 发起支付
     *
     * @param uid
     * @param orderNo
     * @return
     */
    @Transactional
    @GetMapping(value = "/test")
    @ResponseBody
    public KmallResult payTest(
            @RequestAttribute Long uid,
            @NotEmpty(message = "订单号不能为空")
            @RequestParam String orderNo
    ) throws Exception {
        Order order = orderService.getOrder(orderNo);
        if (!OrderStatusEnum.CREATE.getValue().equals(order.getOrderStatus())) {
            throw new ServiceException(KmallResultEnum.ORDER_STATUS_ERROR.getCode(), "【支付异步通知】, 订单状态异常, orderNo=" + orderNo +
                    ", 订单状态=" + order.getStatus());
        }
        if (!order.getUid().equals(uid)) {
            throw new ServiceException(KmallResultEnum.ORDER_STATUS_ERROR);
        }

        // 更新订单表
        order.setOrderStatus(OrderStatusEnum.PAYED.getValue());
        order.setPayTime(new Date());
        orderService.updateOrder(order);

        return KmallResult.success();
    }

    /**
     * 异步回调
     *
     * @param notifyData
     * @return
     * @throws Exception
     */
    @PostMapping(value = "/notify")
    @ResponseBody
    public String notify(@RequestBody String notifyData) throws Exception {
        log.info("【微信支付】异步通知");
        Map<String, String> notifyDataMap = WXPayUtil.xmlToMap(notifyData);
        log.info("notifyData = {}", JsonUtils.toJson(notifyDataMap));

        // 1、验证签名
        WXPay wxPay = new WXPay(wxPayConfig);
        if (!wxPay.isPayResultNotifySignatureValid(notifyDataMap)) {
            throw new ServiceException(KmallResultEnum.PAY_NOTIFY_FAIL.getCode(), "【微信支付】异步通知, 签名验证失败");
        }
        if (!WXPayConstants.SUCCESS.equals(notifyDataMap.get("return_code"))) {
            throw new ServiceException(KmallResultEnum.PAY_NOTIFY_FAIL.getCode(), "【微信支付】异步通知, returnCode != SUCCESS");
        }
        if (!WXPayConstants.SUCCESS.equals(notifyDataMap.get("result_code"))) {
            throw new ServiceException(KmallResultEnum.PAY_NOTIFY_FAIL.getCode(), "【微信支付】异步通知, resultCode != SUCCESS, err_code = "
                    + notifyDataMap.get("err_code") + " err_code_des=" + notifyDataMap.get("err_code_des"));
        }

        // 2、支付的状态
        // 查询订单 判断订单是否存在
        String orderNo = notifyDataMap.get("out_trade_no");
        Order order = orderService.getOrder(orderNo);
        if (!OrderStatusEnum.CREATE.getValue().equals(order.getOrderStatus())){
            throw new ServiceException(KmallResultEnum.PAY_NOTIFY_FAIL.getCode(), "【支付异步通知】, 订单状态异常, orderNo=" + orderNo +
                    ", 订单状态=" + order.getStatus());
        }

        // 3、支付金额 判断金额是否一致(0.10   0.1)
        int totalFee = Integer.parseInt(notifyDataMap.get("total_fee"));
        if (totalFee != order.getPayFee()) {
            throw new ServiceException(KmallResultEnum.PAY_NOTIFY_FAIL.getCode(), "【支付异步通知】异步通知, 订单金额不一致, orderNo="
                    + orderNo + ", 通知金额=" + totalFee + ", 系统金额=" + order.getPayFee());
        }

        // 4、更新订单状态为已支付
        SimpleDateFormat df = new SimpleDateFormat("yyyyMMddHHmmss");
        Date payTime = df.parse(notifyDataMap.get("time_end"));
        String transactionId = notifyDataMap.get("transaction_id");
        order.setPayTime(payTime);
        order.setTradeNo(transactionId);
        order.setOrderStatus(OrderStatusEnum.PAYED.getValue());
        orderService.updateOrder(order);

        return "<xml><return_code><![CDATA[SUCCESS]]></return_code><return_msg><![CDATA[OK]]></return_msg></xml>";
    }

    /**
     * 退款
     *
     * @param orderNo
     * @return
     * @throws Exception
     */
    @GetMapping("/refund")
    @ResponseBody
    public KmallResult refund(@RequestParam String orderNo) throws Exception {
        Order order = orderService.getOrder(orderNo);
        Integer orderStatus = order.getOrderStatus();
        if (OrderStatusEnum.PAYED.getValue().equals(orderStatus)) {
            return KmallResult.build(KmallResultEnum.ORDER_STATUS_ERROR);
        }
        // 退款
        Integer payAmount = order.getPayFee();
        if (payAmount > 0) {
            String transactionId = order.getTradeNo();
            refundWXOrder(order, payAmount, "", wxPayConfig);
        }
        return KmallResult.success();
    }

    /**
     * 退款异步通知
     *
     * @param notifyData
     * @return
     * @throws Exception
     */
    @Transactional
    @PostMapping(value = "/refund/notify")
    @ResponseBody
    public String refundNotify(@RequestBody String notifyData) throws Exception {
        log.info("【微信支付】退款通知");
        Map<String, String> notifyDataMap = WXPayUtil.xmlToMap(notifyData);
        log.info("notifyData = {}", JsonUtils.toJson(notifyDataMap));

        String returnCode = notifyDataMap.get("return_code");
        if ("FAIL".equals(returnCode)) {
            log.info("【微信支付】退款通知失败");
            return "fail";
        }

        // 1、对加密串A做base64解码，得到加密串B
        // 2、对商户key做md5，得到32位小写key* ( key设置路径：微信商户平台(pay.weixin.qq.com)-->账户设置-->API安全-->密钥设置 )
        // 3、用key*对加密串B做AES-256-ECB解密（PKCS7Padding）
        String reqInfo = notifyDataMap.get("req_info");
        String reqInfoData = null;
        try {
            Security.addProvider(new BouncyCastleProvider());
            Cipher cipher = Cipher.getInstance("AES/ECB/PKCS7Padding", "BC");
            SecretKeySpec keySpec = new SecretKeySpec(DigestUtils.md5Hex("v0EyBtt426c08QaNniSJJQGjS9Ts9ewC").toLowerCase().getBytes(), "AES");
            cipher.init(Cipher.DECRYPT_MODE, keySpec);// 初始化
            byte[] data = Base64Utils.decode(reqInfo.getBytes());
            byte[] resultByte = cipher.doFinal(data);
            if (null == resultByte || resultByte.length <= 0) {
                throw new ServiceException(KmallResultEnum.WXC_DECRYPT_FAIL);
            }
            reqInfoData = new String(resultByte, "UTF-8");
        } catch (Exception e) {
            log.error("Exception = {}", e.getMessage());
            throw new ServiceException(KmallResultEnum.WXC_DECRYPT_FAIL);
        }
        Map<String, String> reqInfoDataMap = WXPayUtil.xmlToMap(reqInfoData);
        String refundId = reqInfoDataMap.get("refund_id");
        String refundNo = reqInfoDataMap.get("out_refund_no");
        log.info("reqInfo = {}", reqInfoDataMap);
        OrderRefund orderRefund = orderRefundService.getByRefundNo(refundNo);
        if (orderRefund == null) {
            log.info("退款不存在");
            return "fail";
        }
        if (1 != orderRefund.getRefundStatus()) {
            log.info("退款状态错误");
            return "fail";
        }
        String refundStatus = reqInfoDataMap.get("refund_status");
        if ("SUCCESS".equals(refundStatus)) {
            SimpleDateFormat df = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss");
            Date successTime = df.parse(reqInfoDataMap.get("success_time"));
            orderRefund.setSuccessTime(successTime);
            orderRefund.setOutRefundNo(refundId);
            orderRefund.setRefundStatus(100);
        } else {
            orderRefund.setErrMsg("微信退款失败，refundStatus=" + refundStatus);
            orderRefund.setRefundStatus(2);
        }
        orderRefundService.update(orderRefund);
        return "<xml><return_code><![CDATA[SUCCESS]]></return_code><return_msg><![CDATA[OK]]></return_msg></xml>";
    }

    /**
     * 退款查询
     *
     * @param transactionId
     * @return
     * @throws Exception
     */
    @GetMapping("/refund/query")
    @ResponseBody
    public KmallResult refundQuery(@RequestParam("transaction_id") String transactionId) throws Exception {
        WXPay wxPay = new WXPay(wxPayConfig);
        Map<String, String> reqData = new HashMap<>();
        reqData.put("transaction_id", transactionId);
        Map<String, String> repData = wxPay.refundQuery(reqData);
        return KmallResult.success(repData);
    }

    private void refundWXOrder(Order order, Integer amount, String refundDesc, WXPayConfig wxPayConfig) {
        try {
            if (StringUtils.isEmpty(order.getTradeNo())) {
                return;
            }
            OrderRefund orderRefund = new OrderRefund();
            orderRefund.setOrderNo(order.getOrderNo());
            orderRefund.setUid(order.getUid());
            orderRefund.setAmount(amount);
            orderRefund.setChannel("WXPAY");
            orderRefund.setRefundStatus(OrderRefundStatusEnum.INIT.getValue());
            orderRefund.setRefundDesc(refundDesc);
            orderRefund = orderRefundService.create(orderRefund);

            WXPay wxPay = new WXPay(wxPayConfig);
            Map<String,String> reqData = new HashMap<>();
            reqData.put("transaction_id",order.getTradeNo());
            reqData.put("out_refund_no", orderRefund.getRefundNo());
            reqData.put("total_fee", String.valueOf(amount));
            reqData.put("refund_fee", String.valueOf(amount));
            reqData.put("notify_url", "https://mgg.gzqqs.com/gumiho/wxc/refund/notify");
            Map<String,String> repData = wxPay.refund(reqData);
            String returnCode = repData.get("return_code");
            JsonUtils.toJson(repData);
            if (WXPayConstants.FAIL.equals(returnCode)) {
                log.info("微信退款失败");
                throw new ServiceException(KmallResultEnum.ORDER_REFUND_FAIL);
            }
        } catch (Exception e) {
            log.error(e.getMessage(),e);
            throw new ServiceException(KmallResultEnum.ORDER_REFUND_FAIL);
        }
    }
}
